package org.snail.chat.service.impl;

import lombok.NonNull;
import lombok.extern.log4j.Log4j;
import org.quartz.*;
import org.snail.chat.service.chatchain.ChatCreateOrUpdateChain;
import org.snail.chat.service.chatchain.dao.ChatCreateOrUpdateDAO;
import org.snail.chat.service.chatchain.regular.HandlerManger;
import org.snail.chat.service.chatchain.regular.HandlerRegular;
import org.snail.chat.service.manager.ChainToChatManager;
import org.snail.common.StringUtils2;
import org.snail.common.collection.CollectionUtil;
import org.snail.common.xmlBean.XmlToDocHolder;
import org.snail.constant.GroupConstant;
import org.snail.department.service.impl.DepartmentServiceImpl;
import org.snail.department.service.manager.DeptInfoManager;
import org.snail.dept.bean.TreeNode;
import org.snail.employee.bean.EmpCondition;
import org.snail.employee.service.impl.AddressServiceImpl;
import org.snail.employee.service.impl.EmployeesServiceImpl;
import org.snail.exception.EmpException;
import org.snail.other.bean.GroupDO;
import org.snail.other.mapper.GroupMapper;
import org.snail.other.service.impl.SendMessageServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
@Log4j
@Configuration
@PropertySource("classpath:context-datasource.properties")
public class ChatServiceImpl {

    @Autowired
    private XmlToDocHolder xmlToDocHolder;

    @Autowired
    private GroupMapper groupMapper;

    @Autowired
    private AddressServiceImpl addressService;

    @Autowired
    private EmployeesServiceImpl employeesService;

    @Autowired
    private SendMessageServiceImpl sendMessageService;

    @Autowired
    private DepartmentServiceImpl departmentService;

    @Autowired
    private ChainToChatManager chainToChatManager;

    @Autowired
    private DeptInfoManager deptInfoManager;

    @Autowired
    @Qualifier("scheduler")
    private Scheduler scheduler;

    @Autowired
    @Qualifier("simpleTrigger")
    private SimpleTrigger simpleTrigger;

    @Autowired
    @Qualifier("jobDetail")
    private JobDetail jobDetail;

    /**
     * 同步钉钉方法
     * 1、同步部门及人员信息
     * 2、同步后，根据数据库重新生成部门树
     * 3、同步群组信息
     */
    public void sychronizeUserDepatChat(){
            //同步部门及人员信息
            deptInfoManager.generateChildDeptByRoot();

            //15分钟后执行同步群组，防止和部门同步一起执行
            doCheduleJob();
    }

    /**
     * 15分钟后执行同步群组，防止和部门同步一起执行
     */
    private void doCheduleJob() {
        try {
            JobDetail currentJobDetail = scheduler.getJobDetail(jobDetail.getKey());
            Trigger currentTrigger = scheduler.getTrigger(simpleTrigger.getKey());

            log.error(currentJobDetail + " , " + currentTrigger);

            if(null==currentJobDetail && null==currentTrigger) {
                Date nextTime = genrateNextDate();
                Trigger newTrigger = simpleTrigger.getTriggerBuilder().startAt(nextTime).build();
                scheduler.scheduleJob(jobDetail, newTrigger);
            } else if(null!=currentJobDetail && null!=currentTrigger) {
                Date nextTime = genrateNextDate();
                Trigger newTrigger = currentTrigger.getTriggerBuilder().startAt(nextTime).build();
                scheduler.rescheduleJob(simpleTrigger.getKey(), newTrigger);
            } else if(null!=currentJobDetail && null==currentTrigger) {
                Date nextTime = genrateNextDate();
                Trigger newTrigger = simpleTrigger.getTriggerBuilder().startAt(nextTime).build();
                if(scheduler.checkExists(jobDetail.getKey())) {
                    scheduler.deleteJob(jobDetail.getKey());
                    scheduler.scheduleJob(jobDetail, newTrigger);
                }
            }

            // 启动
            if (!scheduler.isShutdown()) {
                scheduler.start();
                log.error("========================定时器启动成功！");
            }
        } catch (SchedulerException e) {
            log.error("启动队列", e);
        }
    }

    /**
     *  获取下次执行时间
     * @return
     */
    private Date genrateNextDate() {
        Long currentTimeLong = new Date().getTime();
        Long next = 1000*60*15L;
        Date nextTime = new Date(currentTimeLong + next);
        return nextTime;
    }
    /**
     * 同步群组信息
     * @param treeNode
     */
    public void sychronizeChat(TreeNode treeNode){
        log.error("sychronizeChat start");
        try {
            //同步责任链规则
            generateChatCreateOrUpdateHandler();

            //通过责任链，执行不同层级的群组生成
            Map<String, ChatCreateOrUpdateDAO> chatCreateOrUpdateDAOMap = new HashMap<>();
            ChatCreateOrUpdateChain.executeChatCreateOrUpdateChain(treeNode, chatCreateOrUpdateDAOMap,10);

            //同步钉钉Chat信息，并保存信息到数据库
            sychronizeDBAndChatAPI(chatCreateOrUpdateDAOMap);

        } catch (Exception e) {
            log.error("同步群组错误", e);
        }
    }

    /**
     * 同步责任链规则
     */
    private void generateChatCreateOrUpdateHandler(){
        try {
            Map xmlMap = xmlToDocHolder.xmlTransToBean();    //1.执行xml转bean方法

            List<Map> levelRules = new ArrayList<>();
            List<Map> deptIdRules = new ArrayList<>();
            try{
                levelRules = (List)xmlMap.get("levelRules");
                deptIdRules = (List)xmlMap.get("deptIdRules");

                //初始化LV责任链规则
                generateLevelHandler(levelRules);
                //初始化DepartID责任链规则
                generateDeptIdHandler(deptIdRules);

            }catch (Exception e) {
                log.error("数据类型转换错误，在xml转map,在转的时候发生错误", e);
            }
            if (levelRules.isEmpty()) {
                log.error("xml转换的Map为空！");
            }

        } catch (Exception e) {
            log.error("重新生成责任链规则", e);
        }
    }

    /**
     * 生成LV责任链规则
     * @param levelRules
     */
    private void generateLevelHandler(List<Map> levelRules) {
        List<HandlerRegular> handlerRegularList = new ArrayList<HandlerRegular>();
        for (Map map : levelRules) {
            HandlerRegular regular = new HandlerRegular();
            regular.setLvRegular(map.get("levelNum").toString());
            regular.setOwner(map.get("owner").toString());
            regular.setHandlerRegularRule(map.get("currentMethod").toString());
            String otherDept = map.get("otherDept").toString();
            List<String> otherDeptUserIdList = getOtherDeptUserIdList(otherDept);
            regular.setOtherUserIdList(otherDeptUserIdList);
            handlerRegularList.add(regular);
        }
        HandlerManger.generateChatCreateOrUpdateHandler(handlerRegularList);
    }

    /**
     * 生成部门ID责任链规则
     * @param deptIdRules
     */
    private void generateDeptIdHandler(List<Map> deptIdRules) {
        List<HandlerRegular> handlerRegularList = new ArrayList<HandlerRegular>();
        for (Map map : deptIdRules) {
            HandlerRegular regular = new HandlerRegular();
            regular.setLvRegular(map.get("deptId").toString());
            regular.setOwner(map.get("owner").toString());
            regular.setHandlerRegularRule(map.get("currentMethod").toString());
            String otherDept = map.get("otherDept").toString();
            List<String> otherDeptUserIdList = getOtherDeptUserIdList(otherDept);
            regular.setOtherUserIdList(otherDeptUserIdList);
            handlerRegularList.add(regular);
        }
        HandlerManger.generateChatCreateOrUpdateHandler(handlerRegularList);
    }

    /**
     * 获取其他部门人员信息
     * @return
     */
    private List<String> getOtherDeptUserIdList(String otherDept) {
        List<String> otherDeptUserIdList = new ArrayList<String>();
        try {
            if(null!=otherDept && !StringUtils2.isNull(otherDept)) {
                String[] otherDeptArray = otherDept.split(",");
                for (int j = 0; j < otherDeptArray.length; j++) {
                    String oneDept = otherDeptArray[j];
                    if (!StringUtils.isEmpty(oneDept)) {
                        List<String> departUserIdList = departmentService.findUserIdListByDeptId(Long.parseLong(oneDept));
                        log.info("oneDept : " + oneDept + " , departUserIdList : " + departUserIdList);
                        otherDeptUserIdList.addAll(departUserIdList);
                    }
                }
            }
        } catch (EmpException e) {
            log.error("获取其他部门人员信息",  e);
        }
        return otherDeptUserIdList;
    }

    /**
     * 将同步钉钉群组与部门相匹配
     */
    private void sychronizeDBAndChatAPI(Map<String, ChatCreateOrUpdateDAO> chatCreateOrUpdateDAOMap) throws Exception {
        log.error("sychronizeDBAndChatAPI start:"+chatCreateOrUpdateDAOMap);
        //1.获取原始数据
        List<GroupDO> groupsByGroupType = groupMapper.findGroupsByGroupType(GroupConstant.DING_API_TYPE);
        if (null == groupsByGroupType || null == chatCreateOrUpdateDAOMap) {
            log.error("源数据处理异常！异常原因：空指针！");
        } else {
            //2.根据group里面找到相关chat,进行比较 增删查改
            List<List<GroupDO>> groupStatusList = new ArrayList();     //不同状态的群组放在一个list中
            for (GroupDO groupDO : groupsByGroupType) {
                log.error("judgeDBStatus start");
                judgeDBStatus(chatCreateOrUpdateDAOMap, groupDO, groupStatusList);
                log.error("judgeDBStatus end");
            }

            //3.添加新群组及群组下的人员
            log.error("insertGroupAndUserList start");
            insertGroupAndUserList(chatCreateOrUpdateDAOMap, groupStatusList);
            log.error("insertGroupAndUserList end");
            //3.根据list进行相关操作

            executeGroupToDBAndApi(groupStatusList);
        }

        log.error("sychronizeDBAndChatAPI end");
    }

    /**
     *
     * 判断数据库中保存的信息和API获取的信息区别
     *
     * @param chatCreateOrUpdateDAOMap
     * @param groupDO
     * @param groupStatisList
     * @throws Exception
     */
    private void judgeDBStatus(Map<String, ChatCreateOrUpdateDAO> chatCreateOrUpdateDAOMap, GroupDO groupDO,@NonNull List<List<GroupDO>> groupStatisList) throws Exception {
        if (null == groupDO || StringUtils.isEmpty(groupDO.getId())) {   //异常提示
            log.error("群组缺失重要信息！群主详情：" + groupDO);
        } else {
            if (chatCreateOrUpdateDAOMap.containsKey(groupDO.getId() + "")) {    //api中是否有该条数据，有---1.相同数据；2.修改数据

                //从钉钉中获取对应ID的Chat信息,添加到GroupDO中
                ChatCreateOrUpdateDAO chatCreateOrUpdateDAO = chatCreateOrUpdateDAOMap.get(groupDO.getId() + "");
                GroupDO groupDB = new GroupDO();
                groupDB.setOwnerId(chatCreateOrUpdateDAO.getOwnerId());
                groupDB.setGroupName(chatCreateOrUpdateDAO.getName());
                groupDB.setUpdateTime(new Date());
                groupDB.setId(groupDO.getId());

                //从钉钉中获取chat下人员信息
                Set<String> useridlistFromAPI = chatCreateOrUpdateDAO.getUseridlist();

                //从数据库查找对应ID的Chat下人员信息
                List<String> dIngUserIdsByDB = addressService.findDIngUserIdsByGroupId(groupDO.getId());

                //1、与数据库信息相同群组
                log.error("useridlistFromAPI : " + useridlistFromAPI + " , dIngUserIdsByDB : " + dIngUserIdsByDB + " , groupDO : " + groupDO+",chatCreateOrUpdateDAO:"+chatCreateOrUpdateDAO);
                if(null!=chatCreateOrUpdateDAO.getOwnerId() && null!=chatCreateOrUpdateDAO.getName()) {
                    Set<String> dIngUserIdsByDBSet = new HashSet<String>();
                    dIngUserIdsByDBSet.addAll(dIngUserIdsByDB);
                    Set compareCollectionSetAPI =  CollectionUtil.compareCollectionData(dIngUserIdsByDBSet,useridlistFromAPI);
                    Set compareCollectionSetDB =  CollectionUtil.compareCollectionData(useridlistFromAPI,dIngUserIdsByDBSet);
                    if (chatCreateOrUpdateDAO.getOwnerId().equals(groupDO.getOwnerId()) && chatCreateOrUpdateDAO.getName().equals(groupDO.getGroupName())
                            && null!=compareCollectionSetAPI && compareCollectionSetAPI.isEmpty()&&compareCollectionSetDB.isEmpty()) {     //没有变化
                        log.error("没有变化 compareCollectionSetAPI :"+compareCollectionSetAPI+"compareCollectionSetDB :"+compareCollectionSetDB+",groupDO:"+groupDO+",groupStatisList:"+groupStatisList);
                        saveGroupToStatusList(groupStatisList, groupDO, GroupConstant.SAME_GROUP_LIST_INDEX);
                    }else {
                        //2.与数据库不同，需要更新的群组
                        if (!StringUtils.isEmpty(chatCreateOrUpdateDAO.getName()) &&  !chatCreateOrUpdateDAO.getUseridlist().isEmpty()) {
                            if (!StringUtils.isEmpty(groupDO.getChatId())) {
                                groupDB.setChatId(groupDO.getChatId());
                                log.error("有变化 useridlistFromAPI :"+useridlistFromAPI+",dIngUserIdsByDB:"+dIngUserIdsByDB+",groupDO:"+groupDO);
                                //设置要新增和删除的人员信息
                                setAddOrDelUserIds(useridlistFromAPI, dIngUserIdsByDBSet, groupDB);
                                saveGroupToStatusList(groupStatisList, groupDB, GroupConstant.UPDATE_GROUP_LIST_INDEX);
                            } else {
                                log.error("==========================群组缺失相关信息：" + groupDO);
                            }
                        } else {
                            log.error("==========================群组缺失相关信息：" + groupDO);
                        }
                    }
                }
            } else {
                //3.不在新获取的信息中，需要删除的群组
                saveGroupToStatusList(groupStatisList, groupDO, GroupConstant.DELETE_GROUP_LIST_INDEX);
            }
        }
    }

    /**
     * 将群组添加至相应的list中
     * @param groupStatisList
     * @param groupDB
     * @param status
     */
    private void saveGroupToStatusList(@NonNull List<List<GroupDO>> groupStatisList, GroupDO groupDB, Integer status) {
        while (groupStatisList.size() != GroupConstant.TOTAL_COONT){
            groupStatisList.add(new ArrayList<>());
        }
        groupStatisList.get(status).add(groupDB);
    }

    /**
     * 设置要新增和删除的人员信息
     *
     * @param useridlistFromAPI
     * @param dIngUserIdsByDB
     * @param groupDB
     */
    private void setAddOrDelUserIds(Set<String> useridlistFromAPI, Set<String> dIngUserIdsByDB, GroupDO groupDB) {

        Set<String> delUserIds =  CollectionUtil.compareCollectionData(useridlistFromAPI, dIngUserIdsByDB);
        Set<String> addUserIds =  CollectionUtil.compareCollectionData(dIngUserIdsByDB, useridlistFromAPI);

        if (addUserIds.size() != 0) {
            List<String> userAddListDB = new ArrayList<>();
            userAddListDB.addAll(addUserIds);
            groupDB.setAddUserIds(userAddListDB);
        }

        //人工移除组织架构后，需要自动删除人员、及chat关系
        delUserIds = SynEmpAndChatUser(groupDB, delUserIds);
        if (delUserIds.size() != 0) {
            List<String> userDelListDB = new ArrayList<>();
            userDelListDB.addAll(delUserIds);
            groupDB.setDelUserIds(userDelListDB);
        }

        //标记为修改
        groupDB.setSaveType(1);
    }

    /**
     * 人工移除组织架构后，需要自动删除人员、及chat关系
     * @param delUserIds
     */
    private Set SynEmpAndChatUser(GroupDO groupDO, Set<String> delUserIds) {
        Set<String> neeAPIDelUserIds = new HashSet<String>();
        List<String> neeDelUserIds = new ArrayList<>();

        for (String userId: delUserIds) {
            if(!StringUtils2.isNull(userId)) {
                List<String> existList = departmentService.checkUserIdInDepartmentUserList(userId);
                if(null==existList || existList.isEmpty()) {
                    neeDelUserIds.add(userId);
                } else {
                    neeAPIDelUserIds.add(userId);
                }
            }
        }

        log.error("SynEmpAndChatUser Del NotExist User " + neeDelUserIds);
        if(neeDelUserIds.size()>0){
            employeesService.deleteMainEmpListByDingId(neeDelUserIds);
            addressService.deleteAddress(neeDelUserIds, groupDO);
        }
        return neeAPIDelUserIds;
    }

    /**
     * 新增群组
     * 因为严谨问题，还是将新增单独比较，避免api中重复建群的问题
     * @param chatCreateOrUpdateDAOMap
     */
    private void insertGroupAndUserList(@NonNull Map chatCreateOrUpdateDAOMap, List<List<GroupDO>> groupStatusList){
        List<Long> allGroupIds = groupMapper.findALLGroupIds();
        Set deptIdSet = chatCreateOrUpdateDAOMap.keySet();
        List<String> addGroupIdList = new ArrayList(deptIdSet);
        for (Long deptId : allGroupIds) {
            if (deptIdSet.contains(deptId + "")) {
                addGroupIdList.remove(deptId + "");
            }
        }
        for (String deptIdStr : addGroupIdList){
            Long deptId = Long.parseLong(deptIdStr);
            ChatCreateOrUpdateDAO chatCreateOrUpdateDAO = (ChatCreateOrUpdateDAO)chatCreateOrUpdateDAOMap.get(deptIdStr);

            if (null == chatCreateOrUpdateDAO || StringUtils.isEmpty(chatCreateOrUpdateDAO.getName())) {
                log.error("==============================责任链dao缺失重要参数！责任链相关信息！" + chatCreateOrUpdateDAO);
                continue;
            }
            if (null == chatCreateOrUpdateDAO.getUseridlist() || chatCreateOrUpdateDAO.getUseridlist().isEmpty()) {
                log.error("==============================新建群组缺失群成员信息！" + chatCreateOrUpdateDAO);
                continue;
            }
            GroupDO addGroup = new GroupDO();
            addGroup.setCreateTime(new Date());
            addGroup.setGroupType(GroupConstant.DING_API_TYPE);
            addGroup.setId(deptId);
            addGroup.setOwnerId(chatCreateOrUpdateDAO.getOwnerId());
            addGroup.setGroupName(chatCreateOrUpdateDAO.getName());
            //标记为新增
            addGroup.setSaveType(2);
            Set<String> useridSet = chatCreateOrUpdateDAO.getUseridlist();
            List<String> userIdList = new ArrayList<>();
            userIdList.addAll(useridSet);
            addGroup.setAddUserIds(userIdList);

            saveGroupToStatusList(groupStatusList, addGroup, GroupConstant.ADD_GROUP_LIST_INDEX);
        }

    }

    //TODO 需要添加事物
    private void executeGroupToDBAndApi(List<List<GroupDO>> groupStatusList) throws Exception {
        log.error("executeGroupToDBAndApi ``"+groupStatusList);
        if (null == groupStatusList) {
            log.error("同步群组容器为空！");
            return;
        }
        if (null!=groupStatusList.get(GroupConstant.ADD_GROUP_LIST_INDEX) && groupStatusList.get(GroupConstant.ADD_GROUP_LIST_INDEX).size() != 0) {
            List<GroupDO> addGroupUserList = groupStatusList.get(GroupConstant.ADD_GROUP_LIST_INDEX);
            for (GroupDO addGroup : addGroupUserList){
                if (null == addGroup || null == addGroup.getAddUserIds() || addGroup.getAddUserIds().size() == 0 || StringUtils.isEmpty(addGroup.getGroupName()) || StringUtils.isEmpty(addGroup.getOwnerId())){
                    log.error("========================================缺失重要参数！群组添加信息：" + addGroup);
                    continue;
                }
                chainToChatManager.executeDeptQueue(addGroup);
            }
            sendMessageService.simplifySynchonizedGroupsMessage(GroupConstant.INSERT_STATUS, addGroupUserList);
        }

        if (null!=groupStatusList.get(GroupConstant.UPDATE_GROUP_LIST_INDEX) && groupStatusList.get(GroupConstant.UPDATE_GROUP_LIST_INDEX).size() != 0) {
            List<GroupDO> updateGroupUserList = groupStatusList.get(GroupConstant.UPDATE_GROUP_LIST_INDEX);
            for (GroupDO groupDO : updateGroupUserList) {
                if (null == groupDO || StringUtils.isEmpty(groupDO.getGroupName()) || StringUtils.isEmpty(groupDO.getOwnerId())) {
                    log.error("修改钉钉api，缺失相关参数！群组详情：" + groupDO);
                }
               chainToChatManager.executeDeptQueue(groupDO);
            }
            sendMessageService.simplifySynchonizedGroupsMessage(GroupConstant.UPDATE_STATUS, updateGroupUserList);
        }

        if (null!=groupStatusList.get(GroupConstant.DELETE_GROUP_LIST_INDEX) && groupStatusList.get(GroupConstant.DELETE_GROUP_LIST_INDEX).size() != 0) {
            List<GroupDO> delGroupUserList = groupStatusList.get(GroupConstant.DELETE_GROUP_LIST_INDEX);
            sendMessageService.simplifySynchonizedGroupsMessage(GroupConstant.DEL_STATUS, delGroupUserList);
        }
        log.error("executeGroupToDBAndApi  end"+groupStatusList);
    }

    public Map findEmp(){
        Map map =  employeesService.findAllEmp(new EmpCondition());
        return map;
    }
}
